package lastfm
{
  import flash.events.EventDispatcher;
  import vk.api.DataProvider;
  import lastfm.events.ApiEvent;
  import lastfm.events.LastFmEvent;

  /**
   * ...
   * @author Roman Marchenko
   */
  public class LastFm extends EventDispatcher
  {
    public var _artists:Array;
    public var _singleArtistMode:Boolean;

    private var _bannedTracks:Object;
    private var _username:String;
    private var _songmax:Array;
    private var _isBannedTracksLoaded:Boolean;
    private var _trackUpdateEvent:LastFmEvent;

    public function LastFm()
    {
      _singleArtistMode = false;
      _isBannedTracksLoaded = true; // default, to load a track without banned track loading.
    }

    private function reset():void
    {
      _artists = null;
    }

    private function onDefaultError(event:ApiEvent):void
    {
      var newevent:LastFmEvent = new LastFmEvent(LastFmEvent.ERROR);
      newevent.error = event.error["message"];
      dispatchEvent(newevent);
    }

    private function onError(e:ApiEvent):void
    {
      trace("LastFm.onError: " + e.toString());
      if (null == e.error || null == e.error["code"])
      {
        onDefaultError(e);
        return;
      }

      var code:int = int(e.error["code"]);
      switch (code)
      {
        case 6:
          var newevent:LastFmEvent = new LastFmEvent(LastFmEvent.ARTIST_NOT_FOUND);
          //newevent.error = e.error["message"];
          newevent.error = "Запрашиваемый исполнитель не найден";
          dispatchEvent(newevent);
          break;

        default:
          onDefaultError(e);
          break;
      }
    }

    public function getSongMax(artist:String):int
    {
      if (null == _songmax[artist])
      {
        return -1;
      }

      return _songmax[artist];
    }

    public function isBanned(artist:String, track:String):Boolean
    {
      var key:String = artist.toLowerCase();
      if (null == _bannedTracks || null == _bannedTracks[key])
      {
        return false;
      }
      var arr:Array = _bannedTracks[key] as Array;
      var i:int = arr.indexOf(track.toLowerCase());
      return (0 > i ? false : true);
    }

    public function addBanned(artist:String, track:String):void
    {
      if (null == _bannedTracks)
      {
        _bannedTracks = new Object;
      }

      var key:String = artist.toLowerCase();
      if (null == _bannedTracks[key])
      {
        _bannedTracks[key] = new Array;
      }

      var arr:Array = _bannedTracks[key] as Array;
      arr.push(track.toLowerCase());
    }

    private function removeDuplicates(item:*, index:int, array:Array):Boolean
    {
      var toSimpleString:Function = function toSimpleString(str:String):String
      {
        var res:String = str.toLowerCase();
        return res;
      }

      //trace("i=" + index + "; arr.len=" + array.length);
      // Skip the first element and elements after the index.
      var given:String = toSimpleString(item);
      for (var i:int = 0; i < index; ++i)
      {
        var cur:String = toSimpleString(array[i]);
        //trace("- " + given + " -- " + cur);
        if (cur == given)
        {
          return false;
        }
      }

      return true;
    }

    private function onArtistSimilar(event:ApiEvent):void
    {
      trace("LastFm.onArtistSimilar: " + event.response);
      var newevent:LastFmEvent = new LastFmEvent(LastFmEvent.ARTIST_UPDATE);
      newevent.artist = event.response["artist"];

      var artists:Array = event.response["similar"];
      artists.unshift(newevent.artist);
      //_artists.sort();
      artists = artists.filter(removeDuplicates);

      _artists = artists;
      trace("LastFm.onArtistSimilar: total artists to play: " + _artists.length);

      _songmax = new Array;
      var i:int;
      for (i = 0; i < _artists.length; ++i)
      {
        _songmax[_artists[i]] = -1;
      }

      dispatchEvent(newevent);
    }

    private function onArtistGetCorrection(event:ApiEvent):void
    {
      trace("LastFm.onArtistGetCorrection: " + event.response);
      var api:Api = new Api;
      api.addEventListener(lastfm.events.ApiEvent.ERROR, onError);
      api.addEventListener(lastfm.events.ApiEvent.OK, onArtistSearch);
      var s:String = event.response["artist"];
      if (null == s || "" == s)
      {
        // Given artist's name is ok.
        s = String(event.userData);
      }

      api.userData = s;

      // Search lowercase'd string to get aproppriate result.
      api.artistSearch(s.toLowerCase());
    }

    private function onArtistSearch(event:ApiEvent):void
    {
      trace("LastFm.onArtistSearch: " + event.response);
      var v:String = String(event.userData);
      var matches:XMLList = event.response["artistmatches"]["artist"];
      var i:int = 0;
      for (; i < matches.length(); ++i)
      {
        var res:String = String(matches[i]["name"]);
        if (res.toLowerCase() == v.toLowerCase())
        {
          // Found the exactly matched artist.
          break;
        }
      }

      if (i == matches.length())
      {
        // Not found an aproppriate artist.
        var e:LastFmEvent = new LastFmEvent(LastFmEvent.ARTIST_NOT_FOUND);
        dispatchEvent(e);
        return;
      }

      v = String(matches[i]["name"]);
      var api:Api = new Api;
      api.addEventListener(lastfm.events.ApiEvent.ERROR, onError);
      api.addEventListener(lastfm.events.ApiEvent.OK, onArtistSimilar);
      api.userData = v;
      api.artistGetSimilar(v);
    }

    private function onTrack(event:ApiEvent):void
    {
      trace("LastFm.onTrack: " + event.response.toString());
      if (null == _artists)
      {
        trace("LastFm.onTrack: _artists is not yet initialized, call loadArtist before.");
        var neweventerr:LastFmEvent = new LastFmEvent(LastFmEvent.ERROR);
        neweventerr.error = "LastFm.onTrack: _artists is not yet initialized, call loadArtist before.";
        dispatchEvent(neweventerr);
        return;
      }

      var artist:String = event.response["artist"];
      var index:int = _artists.indexOf(artist);
      if (0 <= index)
      {
        _songmax[index] = event.response["max"];
        trace("LastFm.onTrack: songmax=" + _songmax[index]);
      }

      var newevent:LastFmEvent = new LastFmEvent(LastFmEvent.TRACK_UPDATE);
      newevent.artist = artist;
      newevent.song = event.response["track"];
      newevent.image = event.response["image"];

      // If banned tracks haven't been loaded yet then wait for
      // banned tracks loading is complete.
      _trackUpdateEvent = newevent;
      if (_isBannedTracksLoaded)
      {
        dispatchTrackUpdate();
      }
    }

    private function dispatchTrackUpdate():void
    {
      if (_trackUpdateEvent)
      {
        trace("LastFm.signalEvent: " + _trackUpdateEvent.song.toString());
        dispatchEvent(_trackUpdateEvent);
      }
      _trackUpdateEvent = null;
    }

    private function onBannedTracks(event:ApiEvent):void
    {
      var page:uint = uint(event.response.bannedtracks.@page);
      var perPage:uint = uint(event.response.bannedtracks.@perPage);
      var totalPages:uint = uint(event.response.bannedtracks.@totalPages);
      var total:uint = uint(event.response.bannedtracks.@total);

      var tracks:XMLList = event.response.bannedtracks.track;
      for (var i:uint = 0; i < tracks.length(); ++i)
      {
        var artist:String = tracks[i].artist.name;
        artist = artist.toLowerCase();
        var track:String = tracks[i].name.toString().toLowerCase();
        trace("LastFm.onBannedTracks: " + ((page - 1) * perPage + i) + ": " + artist + " - " + track);
        if (null == _bannedTracks[artist])
        {
          _bannedTracks[artist] = new Array;
        }
        _bannedTracks[artist].push(track);
      }

      if (page < totalPages)
      {
        var api:Api = event.target as Api;
        api.userGetBannedTracks(_username, page + 1);
      }
      else
      {
        // Signal that banned tracks are completely loaded.
        _isBannedTracksLoaded = true;
        if (_trackUpdateEvent)
        {
          // TrackUpdate event is ready and waiting for banned tracks,
          // so dispatch it now.
          dispatchTrackUpdate();
        }
      }
    }

    private function onRecentTracks(event:ApiEvent):void
    {
      trace("LastFm.onRecentTracks");
      var arr:Array = new Array;
      var tracks:XMLList = event.response.recenttracks.track;
      for (var i:uint = 0; i < tracks.length(); ++i)
      {
        var track:String = tracks[i].name.toString().toLowerCase();
        arr.unshift(track);
      }

      var e:LastFmEvent = new LastFmEvent(LastFmEvent.RECENT_TRACKS_UPDATE);
      e.recenttracks = arr;
      dispatchEvent(e);
    }

    public function loadInfo(artist:String, singleArtistMode:Boolean = false):void
    {
      trace("LastFm.loadInfo: '" + artist + "', single=" + singleArtistMode);
      reset();
      _singleArtistMode = singleArtistMode;

      var api:Api = new Api;
      api.addEventListener(lastfm.events.ApiEvent.ERROR, onError);
      api.addEventListener(lastfm.events.ApiEvent.OK, onArtistGetCorrection);
      api.userData = artist;
      api.artistGetCorrection(artist);
    }

    public function loadSong(artist:String, tracknum:int):void
    {
      trace("LastFm.loadSong: '" + artist + ", tracknum=" + tracknum);

      var api:Api = new Api;
      api.addEventListener(lastfm.events.ApiEvent.ERROR, onError);
      api.addEventListener(lastfm.events.ApiEvent.OK, onTrack);

      tracknum += 1; // Top tracks start with 1, not with 0.
      api.artistGetTrack(artist, tracknum);
    }

    public function loadBannedTracks(username:String):void
    {
      trace("LastFm.loadBannedTracks: username=" + username);
      _isBannedTracksLoaded = false;
      _bannedTracks = new Object;
      _username = username;
      var api:Api = new Api;
      api.addEventListener(lastfm.events.ApiEvent.ERROR, onError);
      api.addEventListener(lastfm.events.ApiEvent.OK, onBannedTracks);
      api.userGetBannedTracks(username);
    }

    public function loadRecentTracks(username:String, limit:uint):void
    {
      trace("LastFm.loadRecentTracks: username=" + username);
      _username = username;
      var api:Api = new Api;
      api.addEventListener(lastfm.events.ApiEvent.ERROR, onError);
      api.addEventListener(lastfm.events.ApiEvent.OK, onRecentTracks);
      api.userGetRecentTracks(username, limit);
    }

  }

}
